// import com.alibaba.druid.DbType;
// import com.alibaba.druid.sql.ast.SQLName;
// import com.alibaba.druid.sql.ast.SQLPartitionBy;
// import com.alibaba.druid.sql.ast.statement.*;
// import com.alibaba.druid.sql.dialect.oracle.parser.OracleSelectParser;
// import com.alibaba.druid.util.FnvHash;

// import java.util.List;

#include "SQLCreateTableParser.h"
#include "../../DbType.h"
#include "../ast/SQLName.h"
#include "../ast/SQLPartitionBy.h"
#include "../../utils/FnvHash.h"
#include "../dialect/oracle/parser/OracleSelectParser.h"
#include "../ast/statement/SQLCreateTableStatement.h"

SQLCreateTableParser::SQLCreateTableParser(string_ptr sql)
    : SQLDDLParser(sql)
{
}

SQLCreateTableParser::SQLCreateTableParser(SQLExprParser_ptr exprParser)
    : SQLDDLParser(exprParser)
{

  dbType = exprParser->dbType;
}

SQLCreateTableStatement_ptr SQLCreateTableParser::parseCreateTable()
{
  wstring_list_ptr comments = std::make_shared<std::list<wstring_ptr>>();
  if (lexer->isKeepComments() && lexer->hasComment())
  {
    comments = lexer->readAndResetComments();
  }

  SQLCreateTableStatement_ptr stmt = parseCreateTable(BOOL::TRUE);
  if (comments != nullptr)
  {
    stmt->addBeforeComment(comments);
  }

  return stmt;
}

SQLCreateTableStatement_ptr SQLCreateTableParser::parseCreateTable(BOOL_ptr acceptCreate)
{
  SQLCreateTableStatement_ptr createTable = newCreateStatement();
  createTable->setDbType(getDbType());

  if (acceptCreate)
  {
    if (lexer->hasComment() && lexer->isKeepComments())
    {
      createTable->addBeforeComment(lexer->readAndResetComments());
    }

    accept(Token::CREATE);
  }

  if (lexer->identifierEquals(L"GLOBAL"))
  {
    lexer->nextToken();

    if (lexer->identifierEquals(L"TEMPORARY"))
    {
      lexer->nextToken();
      createTable->setType(SQLCreateTableStatement::Type::GLOBAL_TEMPORARY);
    }
    else
    {
      std::string tmp = "syntax error ";
      tmp += encode_util::UnicodeToUTF8(lexer->info()).c_str();
      throw new ParserException(make_string_ptr(tmp));
    }
  }
  else if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str() &&
           lexer->stringVal()->c_str() == L"LOCAL")
  {
    lexer->nextToken();
    if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str() &&
        lexer->stringVal()->c_str() == L"TEMPORAY")
    {
      lexer->nextToken();
      createTable->setType(SQLCreateTableStatement::Type::LOCAL_TEMPORARY);
    }
    else
    {
      std::string tmp = "syntax error ";
      tmp += encode_util::UnicodeToUTF8(lexer->info()).c_str();
      throw new ParserException(make_string_ptr(tmp));
    }
  }

  if (lexer->identifierEquals(FnvHash::Constants::DIMENSION))
  {
    lexer->nextToken();
    createTable->setDimension(BOOL::TRUE);
  }

  accept(Token::TABLE);

  if (lexer->token()->name->c_str() == Token::IF.name->c_str() ||
      lexer->identifierEquals(L"IF"))
  {
    lexer->nextToken();
    accept(Token::NOT);
    accept(Token::EXISTS);

    createTable->setIfNotExiists(BOOL::TRUE);
  }

  createTable->setName(this->exprParser->name());

  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();

    for (;;)
    {
      Token_ptr token = lexer->token();
      if (lexer->identifierEquals(FnvHash::Constants::SUPPLEMENTAL) &&
          DbType::oracle.name->c_str() == dbType->name->c_str())
      {
        SQLTableElement_ptr element = this->parseCreateTableSupplementalLogingProps();
        element->setParent(createTable);
        createTable->getTableElementList()->push_back(element);
      }
      else if (token->name->c_str() == Token::IDENTIFIER.name->c_str() //
               || token->name->c_str() == Token::LITERAL_ALIAS.name->c_str())
      {
        SQLColumnDefinition_ptr column = this->exprParser->parseColumn(createTable);
        column->setParent(createTable);
        createTable->getTableElementList()->push_back(column);
      }
      else if (token->name->c_str() == Token::PRIMARY.name->c_str()   //
               || token->name->c_str() == Token::UNIQUE.name->c_str() //
               || token->name->c_str() == Token::CHECK.name->c_str()  //
               || token->name->c_str() == Token::CONSTRAINT.name->c_str() ||
               token->name->c_str() == Token::FOREIGN.name->c_str())
      {
        SQLConstraint_ptr constraint = this->exprParser->parseConstaint();
        constraint->setParent(createTable);
        createTable->getTableElementList()->push_back(std::dynamic_pointer_cast<SQLTableElement>(constraint));
      }
      else if (token->name->c_str() == Token::TABLESPACE.name->c_str())
      {
        std::string tmp = "TODO ";
        tmp += encode_util::UnicodeToUTF8(lexer->info()).c_str();
        throw new ParserException(make_string_ptr(tmp));
      }
      else
      {
        SQLColumnDefinition_ptr column = this->exprParser->parseColumn();
        createTable->getTableElementList()->push_back(column);
      }

      if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
      {
        lexer->nextToken();

        if (lexer->token()->name->c_str() == Token::RPAREN.name->c_str())
        { // compatible for sql server
          break;
        }
        continue;
      }

      break;
    }

    accept(Token::RPAREN);

    if (lexer->identifierEquals(FnvHash::Constants::INHERITS))
    {
      lexer->nextToken();
      accept(Token::LPAREN);
      SQLName_ptr inherits = this->exprParser->name();
      createTable->setInherits(SQLExprTableSource_ptr(new SQLExprTableSource(inherits)));
      accept(Token::RPAREN);
    }
  }

  if (lexer->token()->name->c_str() == Token::AS.name->c_str())
  {
    lexer->nextToken();

    SQLSelect_ptr select = nullptr;
    if (DbType::oracle.name->c_str() == dbType->name->c_str())
    {
      select = (new OracleSelectParser(this->exprParser))->select();
    }
    else
    {
      select = this->createSQLSelectParser()->select();
    }
    createTable->setSelect(select);
  }

  if (lexer->token()->name->c_str() == Token::WITH.name->c_str() && DbType::postgresql.name->c_str() == dbType->name->c_str())
  {
    lexer->nextToken();
    accept(Token::LPAREN);
    parseAssignItems(createTable->getTableOptions(), createTable, BOOL::FALSE);
    accept(Token::RPAREN);
  }

  if (lexer->token()->name->c_str() == Token::TABLESPACE.name->c_str())
  {
    lexer->nextToken();
    createTable->setTablespace(
        this->exprParser->name());
  }

  if (lexer->token()->name->c_str() == Token::PARTITION.name->c_str())
  {
    SQLPartitionBy_ptr partitionClause = parsePartitionBy();
    createTable->setPartitioning(partitionClause);
  }

  parseCreateTableRest(createTable);

  return createTable;
}

void SQLCreateTableParser::parseCreateTableRest(SQLCreateTableStatement_ptr stmt)
{
}

SQLPartitionBy_ptr SQLCreateTableParser::parsePartitionBy()
{
  return nullptr;
}

SQLTableElement_ptr SQLCreateTableParser::parseCreateTableSupplementalLogingProps()
{
  std::string tmp = "TODO ";
  tmp += encode_util::UnicodeToUTF8(lexer->info()).c_str();
  throw new ParserException(make_string_ptr(tmp));
}

SQLCreateTableStatement_ptr SQLCreateTableParser::newCreateStatement()
{
  return SQLCreateTableStatement_ptr(new SQLCreateTableStatement(getDbType()));
}
